from . import passport_bp
from flask import request, abort, current_app, make_response, jsonify, session
from info import redis, db
from info.utils.captcha.captcha import captcha
from info import constants
from info.lib.yuntongxun.sms import CCP
from info.utils.response_code import RET
import re
import random
from info.models import User
from datetime import datetime


@passport_bp.route("/logout", methods=['POST'])
def logout():
    """退出登陆函数 清除session中的对应登录之后保存的信息"""
    # 当退出登陆的时候 删除session里面保存的信息
    session.pop('user_id', None)
    session.pop('mobile', None)
    session.pop('nick_name', None)
    session.pop('is_admin', False)

    # 设置返回值
    return jsonify(errno=RET.OK, errmsg="OK")


@passport_bp.route('/login', methods=['POST'])
def login():
    """登陆视图函数"""
    '''
    1.获取参数
    1.1 获取手机号码和用户传过来的密码
    2.校验参数
    2.1非空判断
    2.2手机号码的格式校验
    3.逻辑处理
    3.1查询用户的手机号码是否存在
    3.2获取数据库储存的密码真实值 然后与用户传过来的进行判断是否一致
    3.3 一致：登录成功 使用session记录用户信息
    4.返回值处理
    '''

    # 1.1 获取手机号码和用户传过来的密码
    param_dict = request.json
    mobile = param_dict.get('mobile')
    password = param_dict.get('password')

    # 2.1非空判断
    if not all([mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="提交参数不足")

    # 2.2 手机号码格式校验
    if not re.match("^1[3578][0-9]{9}$", mobile):
        return jsonify(errno=RET.DATAERR, errmsg='手机号码格式错误')

    # 3.1查询用户的手机号码是否存在
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="从mysql查询用户异常")

    # 3.2获取数据库储存的密码真实值 然后与用户传过来的进行判断是否一致
    if not user:
        return jsonify(errno=RET.NODATA, errmsg="用户不存在")
    if not user.check_passowrd(password):
        return jsonify(errno=RET.PARAMERR, errmsg="密码填写错误")

    # 3.3 一致：登录成功 使用session记录用户信息
    session['user_id'] = user.id
    session['nick_name'] = user.nick_name
    session['mobile'] = user.mobile
    # 记录最后一次登录时间 (修改对象属性值)
    user.last_login = datetime.now()

    # 对模型对象进行增删改查之后需要再次提交这种变更关联到数据库
    # 如果你不像commit()就要去设置数据库的相关的配置属性（SQLALCHEMY_COMMIT_ON_TEARDOWN = True）
    try:
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        # 失败的时候 记得要数据库回滚
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="提交到数据库异常")

    # 4.返回值处理
    return jsonify(errno=RET.OK, errmsg="登录成功")


@passport_bp.route('/sms_code', methods=["POST"])
def sent_sms():
    # print('sdfsdklfjdsfkldsfjkdfsjdfskldf')
    """获取短信验证码视图函数"""

    """发送短信验证码
     1.获取参数
            1.1 手机号码，用户填写的图片验证码真实值，编号
        2.校验参数
            2.1 判断 手机号码，用户填写的图片验证码真实值，编号是否为空
            2.2 手机号码格式校验
        3.逻辑处理
            3.1 根据编号去获取redis中存储的图片验证码真实值
                3.1.1 image_code_id有值，删除
                3.1.2 image_code_id没有值，表示编号过期
            3.2 对比用户填写的真实值和后端获取的验证码真实值是否一致
            一致：发送短信验证码
            不一致：验证码填写错误
            3.3 保存短信验证码到redis
        4.返回值处理
    """
    # 1.1 先获取用户传过来的手机号码, 图片验证码真实值, 还有编号
    param_dict = request.json

    mobile = param_dict.get('mobile')
    image_code = param_dict.get('image_code')
    image_code_id = param_dict.get('image_code_id')

    # 2.1 判断 手机号码，用户填写的图片验证码真实值，编号是否为空
    if not all([mobile, image_code, image_code_id]):
        # 为空的话 就返回这些数值. 这些数值都是可以定义的
        return jsonify(errno=RET.PARAMERR, errmsg="提交参数不足")

    # 2.2 手机号码格式校验
    if not re.match("^1[3578][0-9]{9}$", mobile):
        return jsonify(errno=RET.DATAERR, errmsg='手机号码格式错误')

    # 3.1 根据编号去获取redis中存储的图片验证码真实值
    try:
        # 获取的数据库保存的图片验证码的真实数据
        # 这边忘记转utf-8
        real_image_code = redis.get(image_code_id)

        # 判断是否相等, 这个是不相等的情况下
        # 查询和判断的过程中, 都要把字符串转成小写. 不然用户写小写的话. 验证会失败的
        if real_image_code.lower() != image_code.lower():
            return jsonify(error=RET.DATAERR, errmsg='图片验证码填写错误')
    except Exception as e:
        current_app.logger.error(e)
        # 获取图片验证码失败
        return jsonify(errno=RET.DBERR, errmsg="获取图片验证码失败")

    # 3.1.2 image_code_id没有值，表示编号过期
    if not real_image_code:
        return jsonify(errno=RET.NODATA, errmsg="验证码真实值过期")

    # 3.2 对比用户填写的真实值和后端获取的验证码真实值是否一致
    if image_code.lower() != real_image_code.lower():
        return jsonify(errno=RET.DATAERR, errmsg="图片验证码填写错误")

    # 一致：发送短信验证码
    # 查看手机号码是否注册过
    try:
        # 在表格对象里面查询
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as e:
        return jsonify(errno=RET.DATAERR, errmsg="查询用户手机号码是否存在异常")
        # 已经注册过了
    if user:
        return jsonify(errno=RET.DATAEXIST, errmsg="用户已经注册过")

    # 3.3 保存短信验证码到redis
    # 3.3.1. 生成短信验证码随机值
    sms_code = random.randint(0, 999999)
    sms_code = '%06d' % sms_code
    # 3.3.2. 调用云通讯发送短信验证码
    result = CCP().send_template_sms(mobile, [sms_code, constants.SMS_CODE_REDIS_EXPIRES / 60], 1)

    # 短信发送不成功的情况下
    if result != 0:
        return jsonify(errno=RET.THIRDERR, errmsg="发送短信验证码失败")

    try:
        # 发送成功的情况下 保存到数据库
        redis.set("SMS_%s" % mobile, sms_code, ex=constants.SMS_CODE_REDIS_EXPIRES)

    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="存储短信验证码真实值失败")

        # 返回值处理
    return jsonify(errno=RET.OK, errmsg="发送短信验证码成功，请注意查收")


@passport_bp.route('/register', methods=['POST'])
def register():
    '''注册管理'''
    """
        1. 获取参数和判断是否有值
        2. 从redis中获取指定手机号对应的短信验证码的
        3. 校验验证码
        4. 初始化 user 模型，并设置数据并添加到数据库
        5. 保存当前用户的状态
        6. 返回注册的结果
        :return:
    """

    # 1. 获取参数和判断是否有值
    json_data = request.json
    mobile = json_data.get('mobile')
    sms_code = json_data.get('smscode')
    password = json_data.get('password')

    if not all([mobile, sms_code, password]):
        return jsonify(errno=RET.PARAMERR, errmsg='参数不全')

    # 判断手机号码的格式是否正确
    if not re.match("^1[3578][0-9]{9}$", mobile):
        return jsonify(errno=RET.DATAERR, errmsg='手机号码格式错误')

    # 2. 从redis中获取指定手机号对应的短信验证码的
    try:
        real_sms_code = redis.get('SMS_%s' % mobile)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="获取本地验证码失败")

    # 如果短信验证码不存在的情况下:
    if not real_sms_code:
        return jsonify(erron=RET.DATAERR, errmsg='短信验证码已经过期')

    # 3. 校验验证码
    # 如果验证失败的情况下 删除短信验证码
    if sms_code != real_sms_code:
        return jsonify(errno=RET.DATAERR, errmsg='短信验证码错误')

    try:
        redis.delete('MSM_$s' % mobile)
    except Exception as e:
        current_app.logger.error(e)

    # 4. 初始化 user 模型，并设置数据并添加到数据库
    user = User()
    user.nick_name = mobile
    #  对密码进行加密处理, 并保存
    user.password = password
    user.mobile = mobile
    # 获取当前时间作为最后一次登录时间
    user.last_login = datetime.now()

    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg="数据保存错误")

    # 5. 保存当前用户的状态 在flask里面导入

    session["user_id"] = user.id
    session["nick_name"] = user.nick_name
    session["mobile"] = user.mobile

    # 6. 返回注册结果
    return jsonify(errno=RET.OK, errmsg="OK")


# 127.0.0.1:5000/passport/image_code?code_id=编号
@passport_bp.route("/image_code")
def get_imagecode():
    """
    图片验证码的后端接口(GET)
    1. 获取参数
        1.1 获取前端携带的imageCodeId编号
    2. 校验参数
        2.1 imageCodeId编号是否有值
    3. 逻辑处理
        3.0 生成验证码图片&验证码图片的真实值
        3.1 使用imageCodeId编号作为key存储生成的图片验证码真实值
    4. 返回值处理
        4.1 返回生成的图片
    """
    # 1.1 获取前端携带的imageCodeId编号  get请求都是成对存在的, 用他的键, 就可以获取它的值
    # 127.0.0.1:5000/passport/image_code?code_id=编号
    ImageCodeID = request.args.get('code_id')
    # 判断接收到的ImageCodeID是否为空, 空的话, 就返回404. 否则调用函数, 去创建图片验证码

    if not ImageCodeID:
        abort(404)
    # 3.0 生成验证码图片&验证码图片的真实值
    name, text, image = captcha.generate_captcha()
    try:
        redis.set(ImageCodeID, text, ex=constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)
        abort(500)

    # 4. 返回值处理
    # 4.1 返回生成的图片
    respose = make_response(image)
    respose.headers["Content-Type"] = "image/jpg"
    print(text)
    return respose
